----------------------------------------------------------------------------------
-- Project 2, Part 2
-- Jason Parrott
-- Rodney McGee
--
-- Main loop
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_ARITH.all;
use IEEE.STD_LOGIC_UNSIGNED.all;

entity program is
  port ( clk            : in  std_logic;
         reset          : in  std_logic;
         keyboard_ascii : in  std_logic_vector (7 downto 0);
         keyboard_ready : in  std_logic;
         ascii_code     : out std_logic_vector (7 downto 0);
         lcd_data_ready : out std_logic;
         leds           : out std_logic_vector (7 downto 0);
         tx             : out std_logic;
         rx             : in  std_logic
         );
end program;

architecture Behavioral of program is


-- component program_tx is
-- Port ( data_in : in STD_LOGIC_VECTOR (127 downto 0);
-- clk : in STD_LOGIC;
-- reset : in STD_LOGIC;
-- w_data : out STD_LOGIC_VECTOR (7 downto 0);
-- tx_full : in STD_LOGIC;
-- wr_uart : out STD_LOGIC;
-- data_available : in STD_LOGIC;
-- data_ack : out STD_LOGIC);
-- end component;

--component program_rx is
-- port ( clk : in std_logic;
-- reset : in std_logic;
-- data_out : out std_logic_vector (127 downto 0);
-- has_data : out std_logic;
-- rd_ack : in std_logic;
-- rd_uart : out std_logic;
-- rx_empty : in std_logic;
-- r_data : in std_logic_vector (7 downto 0));
--end component;

  component uart is
                   generic(
                     -- Default setting:
                     -- 19,200 baud, 8 data bis, 1 stop its, 2^2 FIFO
                     DBIT            :     integer := 8;  -- # data bits
                     SB_TICK         :     integer := 16;  -- # ticks for stop bits, 16/24/32
                     --   for 1/1.5/2 stop bits
                     DVSR            :     integer := 163;  -- baud rate divisor
                     -- DVSR = 50M/(16*baud rate)
                     DVSR_BIT        :     integer := 8;  -- # bits of DVSR
                     FIFO_W          :     integer := 2  -- # addr bits of FIFO
                     -- # words in FIFO=2^FIFO_W
                     );
                 port(
                   clk, reset        : in  std_logic;
                   rd_uart, wr_uart  : in  std_logic;
                   rx                : in  std_logic;
                   w_data            : in  std_logic_vector(7 downto 0);
                   tx_full, rx_empty : out std_logic;
                   r_data            : out std_logic_vector(7 downto 0);
                   tx                : out std_logic
                   );
  end component;

  signal wait_counter   : std_logic_vector (26 downto 0);  --2 sec - 101111101011110000100000000
  signal shortcounter   : std_logic_vector (24 downto 0);  --.5 sec - 1011111010111100001000000
  signal tinycounter    : std_logic_vector (10 downto 0);
  signal allow_keyboard : std_logic;

  signal welcome_message       : std_logic_vector (127 downto 0);
  signal password_message      : std_logic_vector (127 downto 0);
  signal accessgranted_message : std_logic_vector (127 downto 0);
  signal accessdenied_message  : std_logic_vector (127 downto 0);
  signal encode_message        : std_logic_vector (127 downto 0);
  signal decode_message        : std_logic_vector (127 downto 0);
  signal message_buffer        : std_logic_vector (127 downto 0);
  signal reprint_buffer        : std_logic_vector (127 downto 0);

  signal string_buffer  : std_logic_vector (127 downto 0);
  signal receive_buffer : std_logic_vector (127 downto 0);

  signal uart_input        : std_logic_vector (127 downto 0);
  signal uart_output       : std_logic_vector (127 downto 0);
  signal uart_input_ready  : std_logic;
  signal uart_output_ready : std_logic;
  signal uart_input_ack    : std_logic;
  signal uart_output_ack   : std_logic;



  signal tx_full, rx_empty, rd_uart, wr_uart : std_logic;
  signal w_data, r_data                      : std_logic_vector (7 downto 0);

  signal led_bank         : std_logic_vector (7 downto 0);
  signal leds_to_receiver : std_logic_vector (7 downto 0);

  type statetype is ( S1, S11, S12, S13, S14, S15, S16,  -- Print out welcome message
                      S4, S41, S42, S43, S47, S48,  -- Encrypt/Decrypt Prompt
                      S5, S51,  --S52,  -- Display crypto result
                                                      --S5, S51, S52, S53, S54, S55, S56, S57, S58, S59, S5A,  -- Display crypto result
                                                      --S6, S61,  -- Pause for keyboard
                      S7, S71, S72, S73, S74, S76, S77, S78, S79,
                      S99);
  signal state, next_state : statetype := S1;

begin

  --uart_instance: entity work.uart(arch)
  uart_instance : entity uart
-- generic map(DVSR => 50000000, DVSR_BIT => 26)
    port map(clk     => clk, reset => reset, rd_uart => rd_uart,
             wr_uart => wr_uart, rx => rx, w_data => w_data,
             tx_full => tx_full, rx_empty => rx_empty,
             r_data  => r_data, tx => tx);
  --r_data=>r_data, tx=>leds(0));

  program_transmitter : entity work.program_tx(arch)
    port map(clk => clk, reset => reset, w_data => w_data, wr_uart => wr_uart, tx_full => tx_full, data_in => uart_input, data_available => uart_input_ready, data_ack => uart_input_ack );

  program_reciever : entity work.program_rx(arch)
    port map(clk => clk, reset => reset, data_out => uart_output, has_data => uart_output_ready, rd_ack => uart_output_ack, rd_uart => rd_uart, rx_empty => rx_empty, r_data => r_data, leds => leds_to_receiver);

-- Port mappings for addtional modules needed within the program loop

  state <= next_state;

-- leds <= led_bank;
  leds <= leds_to_receiver;

  process (clk, reset) is
                         variable string_counter : std_logic_vector (7 downto 0) := "00000000";
                       variable   line_counter   : std_logic_vector (5 downto 0) := "000000";
  begin
    if (reset = '1') then
      next_state     <= S1;
      string_counter                                                             := "00000000";
      lcd_data_ready <= '0';

      allow_keyboard <= '0';
      line_counter := "000000";

      wait_counter <= "000000000000000000000000000";
      shortcounter <= "0000000000000000000000000";
      tinycounter  <= "00000000000";

      welcome_message       <= x"57656c636f6d65204350454734323221";
      password_message      <= x"456e7465722050617373776f72643a20";
      accessgranted_message <= x"416363657373204772616e7465642120";
      accessdenied_message  <= x"4163636573732044656e696564203d28";
      decode_message        <= x"5465787420746f206465636f64653a20";
      encode_message        <= x"5465787420746f20656e636f64653a20";

      receive_buffer <= x"20202020202020202020202020202020";
      string_buffer  <= x"00000000000000000000000000000000";


      uart_input       <= x"00000000000000000000000000000000";
      uart_input_ready <= '0';
      uart_output_ack  <= '0';
    elsif (rising_edge(clk)) then
-- leds <= std_logic_vector(line_counter) & "00";
      case state is

                                        -- ===============================================
                                        -- State 1: Print Welcome Message
                                        -- ===============================================

                                        -- Wait .5 seconds
        when S1 =>
          led_bank       <= x"10";
          if(shortcounter = "1011111010111100001000000") then
            shortcounter <= "0000000000000000000000000";
            next_state   <= S11;
          else
            shortcounter <= shortcounter + 1;
            next_state   <= S1;
          end if;

                                        -- Print out welcome message
        when S11 =>
          led_bank       <= x"11";
          ascii_code     <= welcome_message(127 downto 120);
          lcd_data_ready <= '1';

          welcome_message <= welcome_message(119 downto 0) & "00000000";
          string_counter := string_counter + 8;
          next_state      <= S12;

        when S12 =>
          led_bank         <= x"12";
          if(tinycounter = "11111111111") then
            tinycounter    <= "00000000000";
            lcd_data_ready <= '0';
            if(string_counter = x"80") then
              string_counter := "00000000";
              next_state   <= S13;
            else
              next_state   <= S11;
            end if;
          else
            tinycounter    <= tinycounter + 1;
            next_state     <= S12;
          end if;

                                        -- Wait 2 seconds
        when S13 =>
          led_bank       <= x"13";
          if(wait_counter = "101111101011110000100000000") then
            wait_counter <= "000000000000000000000000000";
            next_state   <= S14;
          else
            wait_counter <= wait_counter + 1;
            next_state   <= S13;
          end if;

                                        -- Erase screen
        when S14 =>
          led_bank       <= x"14";
          ascii_code     <= x"0D";
          lcd_data_ready <= '1';
          next_state     <= S15;

        when S15 =>
          led_bank         <= x"15";
          if(tinycounter = "11111111111") then
            tinycounter    <= "00000000000";
            lcd_data_ready <= '0';
            next_state     <= S16;
          else
            tinycounter    <= tinycounter + 1;
            next_state     <= S15;
          end if;

                                        -- Wait .5 seconds
        when S16 =>
          led_bank       <= x"16";
          if(shortcounter = "1011111010111100001000000") then
            shortcounter <= "0000000000000000000000000";
            next_state   <= S4;
          else
            shortcounter <= shortcounter + 1;
            next_state   <= S16;
          end if;

                                        -- ===============================================
                                        -- State 4: Encode or Decode Input
                                        -- ===============================================

                                        -- Wait 2 seconds
        when S4 =>
          led_bank       <= x"40";
          if(wait_counter = "101111101011110000100000000") then
            wait_counter <= "000000000000000000000000000";
            next_state   <= S41;
          else
            wait_counter <= wait_counter + 1;
            next_state   <= S4;
          end if;

                                        -- Erase screen
        when S41 =>
          led_bank       <= x"41";
          ascii_code     <= x"0D";
          lcd_data_ready <= '1';
          next_state     <= S42;

        when S42 =>
          led_bank         <= x"42";
          if(tinycounter = "11111111111") then
            tinycounter    <= "00000000000";
            lcd_data_ready <= '0';
            next_state     <= S43;
          else
            tinycounter    <= tinycounter + 1;
            next_state     <= S42;
          end if;

                                        -- Wait .5 seconds
        when S43 =>
          led_bank         <= x"43";
          if(shortcounter = "1011111010111100001000000") then
            shortcounter   <= "0000000000000000000000000";
            allow_keyboard <= '1';
            next_state     <= S7;
          else
            shortcounter   <= shortcounter + 1;
            next_state     <= S43;
          end if;

                                        -- Print out ENCODE/DECODE Prompt
--                                      when S45 =>
--                                              ascii_code <= message_buffer(127 downto 120);
--                                              lcd_data_ready <= '1';
--                                              
--                                              message_buffer <= message_buffer(119 downto 0) & "00000000";
--                                              string_counter := string_counter + 8;
--                                              next_state <= S46;
--                                              
--                                      when S46 =>
--                                              if(tinycounter = "11111111111") then
--                                                      tinycounter <= "00000000000";
--                                                      lcd_data_ready <= '0';
--                                                      if(string_counter = x"80") then
--                                                              string_counter := "00000000";
--                                                              allow_keyboard <= '1';
--                                                              next_state <= S47;
--                                                      else
--                                                              next_state <= S45;
--                                                      end if;
--                                              else
--                                                      tinycounter <= tinycounter + 1;
--                                                      next_state <= S46;
--                                              end if;

                                                -- Process keyboard input
        when S47 =>
          led_bank            <= x"47";
          allow_keyboard      <= '1';
          if(uart_output_ready = '1') then
            receive_buffer    <= uart_output;
            uart_output_ack   <= '1';
            next_state        <= S7;
          elsif(keyboard_ready = '1' and allow_keyboard = '1') then
            if(keyboard_ascii = x"0D") then     -- Enter/Return
              line_counter   := "000000";
              allow_keyboard  <= '0';
              next_state      <= S5;
            elsif(keyboard_ascii = x"08") then  -- Backspace
              if line_counter /= "000000" then
                string_buffer <= x"00" & string_buffer(127 downto 8);
                line_counter := line_counter - 1;

                ascii_code     <= keyboard_ascii;  -- Put backspace to lcd to 'backup' cursor
                lcd_data_ready <= '1';
                next_state     <= S48;
              else
                next_state     <= S47;
              end if;
            elsif(keyboard_ascii = x"00") then  -- Unknown
              next_state       <= S47;
            else
              if line_counter /= "010000" then
                string_buffer  <= string_buffer(119 downto 0) & keyboard_ascii;

                ascii_code     <= keyboard_ascii;
                line_counter := line_counter + 1;
                lcd_data_ready <= '1';
                next_state     <= S48;
              else
                next_state     <= S47;
              end if;
            end if;
          else
            lcd_data_ready     <= '0';
            next_state         <= S47;
          end if;

        when S48 =>
          led_bank         <= x"48";
          lcd_data_ready   <= '0';
          if line_counter = "010000" then
            line_counter := "000000";
            allow_keyboard <= '0';
            next_state     <= S5;
          else
            next_state     <= S47;
          end if;


                                        -- Send our string to the uart
        when S5 =>
          led_bank         <= x"50";
          uart_input       <= string_buffer;
          uart_input_ready <= '1';
          next_state       <= S51;


          --when S51 =>
          -- if(string_counter /= line_counter) then

        when S51 =>
          led_bank         <= x"51";
          uart_input_ready <= '0';
          if(uart_input_ack = '0') then
            next_state     <= S51;
          else
            string_buffer  <= x"00000000000000000000000000000000";
            next_state     <= S7;
          end if;


                                        -- Wait for uart to be finished
--                                      when S52 =>
--                                              led_bank <= x"52";
--                                              if(uart_output_ready = '1') then
--                                                      next_state <= S47;
--                                              else
--                                                      next_state <= S52;
--                                              end if;

                                        -- Wait .5 seconds
--                                      when S58 =>
--                                              led_bank <= x"58";
--                                              if(shortcounter = "1011111010111100001000000") then
--                                                      shortcounter <= "0000000000000000000000000";
--                                                      next_state <= S59;
--                                              else
--                                                      shortcounter <= shortcounter + 1;
--                                                      next_state <= S58;
--                                              end if;


                                        -- ===============================================
                                        -- State 6: Stay in this state - Wait for keyboard
                                        -- ===============================================
--                                      when S6 =>
--                                              led_bank <= x"60";
--                                              if (keyboard_ready = '1') then
--                                                      if (allow_keyboard = '1') then
--                                                              next_state <= S61;
--                                                      end if;
--                                              else
--                                                      next_state <= S6;
--                                              end if;
--                                                        
--                                      when S61 =>
--                                              led_bank <= x"61";
--                                              allow_keyboard <= '0';
--                                              next_state <= S41;



                                        -- ===============================================
                                        -- State 7: print the screen when we receive data
                                        -- ===============================================

                                        -- clear screen
        when S7 =>
          led_bank         <= x"70";
          --if(tinycounter = "11111111111") then
          --  tinycounter    <= "00000000000";
          if(shortcounter = "1011111010111100001000000") then
            shortcounter   <= "0000000000000000000000000";
            lcd_data_ready <= '0';
            next_state     <= S71;
          else
            --tinycounter    <= tinycounter + 1;
            shortcounter   <= shortcounter + 1;
            next_state     <= S7;
          end if;

          uart_output_ack <= '0';

        when S71 =>
          led_bank       <= x"71";
          ascii_code     <= x"0D";
          lcd_data_ready <= '1';
          next_state     <= S79;
          reprint_buffer <= receive_buffer;


        when S79 =>
          led_bank         <= x"79";
          if(tinycounter = "11111111111") then
            tinycounter    <= "00000000000";
            lcd_data_ready <= '0';
            next_state     <= S78;
          else
            tinycounter    <= tinycounter + 1;
            next_state     <= S79;
          end if;

                                        -- Wait .5 seconds
        when S78 =>
          led_bank       <= x"78";
          if(shortcounter = "1011111010111100001000000") then
            shortcounter <= "0000000000000000000000000";
            next_state   <= S72;
          else
            shortcounter <= shortcounter + 1;
            next_state   <= S78;
          end if;

                                        -- print newly recieved message
        when S72 =>
          led_bank          <= x"72";
          if(reprint_buffer(7 downto 0) = x"00") then
               ascii_code   <= x"20";
             else
               ascii_code   <= reprint_buffer(7 downto 0);
             end if;
             lcd_data_ready <= '1';

             reprint_buffer <= x"20" & reprint_buffer(127 downto 8);
             string_counter := string_counter + 8;
             next_state     <= S73;

             when S73 =>
             led_bank         <= x"73";
             if(tinycounter = "11111111111") then
               tinycounter    <= "00000000000";
               lcd_data_ready <= '0';
               if(string_counter = x"80") then
                 string_counter := "00000000";
                                        --allow_keyboard <= '1';
                 next_state   <= S74;
               else
                 next_state   <= S72;
               end if;
             else
               tinycounter    <= tinycounter + 1;
               next_state     <= S73;
             end if;

                                        -- reprint user input
             when S74 =>
             led_bank         <= x"74";
             if(line_counter = "000000") then
               next_state     <= S47;
             else
               reprint_buffer <= string_buffer;
               next_state     <= S76;
             end if;

             when S76 =>
             led_bank       <= x"76";
             ascii_code     <= reprint_buffer(127 downto 120);
             lcd_data_ready <= '1';
             reprint_buffer <= reprint_buffer(119 downto 0) & x"00";
             string_counter := string_counter + 8;
             next_state     <= S77;

             when S77 =>
             led_bank           <= x"77";
             if(tinycounter = "11111111111") then
               tinycounter      <= "00000000000";
               lcd_data_ready   <= '0';
               if(string_counter = x"80") then
                 string_counter := "00000000";
                 allow_keyboard <= '1';
                 next_state     <= S47;
               else
                 next_state     <= S76;
               end if;
             else
               tinycounter      <= tinycounter + 1;
             end if;

                                        -- ===============================================
                                        -- State 99: Infinite Loop
                                        -- ===============================================
             when S99 =>
             led_bank   <= x"99";
             next_state <= S99;



             end case;
             end if;
             end process;


             end Behavioral;

